
import helpers.Tripple;

import java.awt.Dimension;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;

import javax.imageio.ImageIO;
import javax.swing.JCheckBox;
import javax.swing.JDesktopPane;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JSeparator;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.filechooser.FileFilter;

import viewers.ControlPanel;
import viewers.FilteredImageViewer;
import viewers.OriginalImageViewer;
import viewers.ProjectedHoughSpaceViewer;
import viewers.TableViewer;
import viewers.TransformViewer;


import models.HoughTransformModel;



/**
 * Main desktop for the Hough Circle Detector Program
 * @author benjamin
  
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
public class HoughCircleDetector extends JFrame implements ActionListener,ChangeListener{
	private JDesktopPane desktop;
	private TransformViewer transformViewer;
	private OriginalImageViewer originalImageViewer;
	private FilteredImageViewer filteredImageViewer;
	private ProjectedHoughSpaceViewer projectedHoughSpaceViewer;
	private ControlPanel controlPanel;
	private TableViewer tableViewer;
	private JMenuBar menuBar;
	private JMenu fileMenu;
	private JMenu windowMenu;
	private JMenuItem loadImage;
	private JMenuItem saveFiltered;
	private JMenuItem saveHough;
	private JMenuItem saveResulting;
	private JMenuItem saveCircleCoords;
	private JCheckBox showOriginalImage;
	private JCheckBox showFilteredImage;
	private JCheckBox showHoughImage;
	private JCheckBox showDetectedCircleImage;
	private JCheckBox showControlPanel;
	private JCheckBox showCircleCoords;
	private JMenuItem exit;
	private JFileChooser fileChooser;
	private FileFilter imageFiles;
	private FileFilter csvFiles;
	private FileFilter pngFiles;
	public static final HoughTransformModel houghTransformer = new HoughTransformModel();
	/**
	 * Default constructor
	 * Sets up the components in the viewer including MDI windows and menus
	 */
	public HoughCircleDetector(){
		super("Hough Circle Detector");
		GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
		Dimension screenSize = new Dimension(gd.getDisplayMode().getWidth(),gd.getDisplayMode().getHeight());
		setBounds(new Rectangle(screenSize.width,screenSize.height));
		//set up the dockable region:
		desktop = new JDesktopPane();
		menuBar = new JMenuBar();
		fileMenu = new JMenu("File");
		windowMenu = new JMenu("Windows");
		loadImage = new JMenuItem("Load Image");
		saveFiltered = new JMenuItem("Export Filtered Image");
		saveHough = new JMenuItem("Export Hough Projection");
		saveResulting = new JMenuItem("Export Detected Circle Image");
		saveCircleCoords = new JMenuItem("Export Circle Coordinates As CSV");
		showOriginalImage = new JCheckBox("Original Image");
		showFilteredImage = new JCheckBox("Filtered Image");
		showHoughImage = new JCheckBox("Hough Transform Projection");
		showDetectedCircleImage = new JCheckBox("Detected Circle Representation");
		showControlPanel = new JCheckBox("Control Panel");
		showCircleCoords = new JCheckBox("Circle Coordinate Table");
		fileChooser = new JFileChooser();
		//set up some filters:
		imageFiles = new FileFilter() {
			
			@Override
			public String getDescription() {
				return "All Supported Images (tiff,tif,gif,jpeg,jpg,png)";
			}
			
			@Override
			public boolean accept(File f) {
				if (f.isDirectory())
					return true;
				int extIndex = f.getName().lastIndexOf(".");
				if (extIndex > 0 && extIndex < f.getName().length()) {
					String extension = f.getName().substring(extIndex + 1)
							.toLowerCase();
					if (extension != null) {
						if (extension.equals("tiff") || extension.equals("tif")
								|| extension.equals("gif")
								|| extension.equals("jpeg")
								|| extension.equals("jpg")
								|| extension.equals("png")) {
							return true;
						} else {
							return false;
						}
					}
				}
		 
		        return false;
			}
		};
		csvFiles = new FileFilter() {
			
			@Override
			public String getDescription() {
				return "Comma Separated Text (csv)";
			}
			
			@Override
			public boolean accept(File f) {
				if (f.isDirectory())
					return true;
				int extIndex = f.getName().lastIndexOf(".");
				if (extIndex > 0 && extIndex < f.getName().length()) {
					String extension = f.getName().substring(extIndex + 1)
							.toLowerCase();
					if (extension != null) {
						if (extension.equals("csv")) {
							return true;
						} else {
							return false;
						}
					}
				}
		 
		        return false;
			}
		};
		pngFiles = new FileFilter() {
			
			@Override
			public String getDescription() {
				return "Portable Network Graphics (png)";
			}
			
			@Override
			public boolean accept(File f) {
				if (f.isDirectory())
					return true;
				int extIndex = f.getName().lastIndexOf(".");
				if (extIndex > 0 && extIndex < f.getName().length()) {
					String extension = f.getName().substring(extIndex + 1)
							.toLowerCase();
					if (extension != null) {
						if (extension.equals("png")) {
							return true;
						} else {
							return false;
						}
					}
				}
		 
		        return false;
			}
		};
		fileChooser.setFileFilter(imageFiles);
		loadImage.addActionListener(this);
		exit = new JMenuItem("Exit");
		exit.addActionListener(this);
		setContentPane(desktop);
		menuBar.add(fileMenu);
		fileMenu.add(loadImage);
		fileMenu.add(new JSeparator());
		fileMenu.add(saveFiltered);
		fileMenu.add(saveHough);
		fileMenu.add(saveResulting);
		fileMenu.add(saveCircleCoords);
		saveFiltered.addActionListener(this);
		saveHough.addActionListener(this);
		saveResulting.addActionListener(this);
		saveCircleCoords.addActionListener(this);
		fileMenu.add(new JSeparator());
		fileMenu.add(exit);
		menuBar.add(windowMenu);
		windowMenu.add(showCircleCoords);
		windowMenu.add(showControlPanel);
		windowMenu.add(showDetectedCircleImage);
		windowMenu.add(showFilteredImage);
		windowMenu.add(showHoughImage);
		windowMenu.add(showOriginalImage);
		showCircleCoords.setSelected(true);
		showControlPanel.setSelected(true);
		showDetectedCircleImage.setSelected(true);
		showFilteredImage.setSelected(true);
		showHoughImage.setSelected(true);
		showOriginalImage.setSelected(true);
		showCircleCoords.addChangeListener(this);
		showControlPanel.addChangeListener(this);
		showDetectedCircleImage.addChangeListener(this);
		showFilteredImage.addChangeListener(this);
		showHoughImage.addChangeListener(this);
		showOriginalImage.addChangeListener(this);
		setJMenuBar(menuBar);
		desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
		transformViewer = new TransformViewer("Detected Circles Viewer",true);
		originalImageViewer = new OriginalImageViewer("Loaded Image Viewer",true);
		filteredImageViewer = new FilteredImageViewer("Filtered Image Viewer",true);
		controlPanel = new ControlPanel("Control Panel",true, houghTransformer);
		tableViewer = new TableViewer("Detected Circle Properties",true);
		projectedHoughSpaceViewer = new ProjectedHoughSpaceViewer("Projected Hough Space", true);
		desktop.add(originalImageViewer);
		desktop.add(transformViewer);
		desktop.add(filteredImageViewer);
		desktop.add(controlPanel);
		desktop.add(projectedHoughSpaceViewer);
		desktop.add(tableViewer);
		JFrame.setDefaultLookAndFeelDecorated(true);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		houghTransformer.registerObserver(originalImageViewer);
		houghTransformer.registerObserver(filteredImageViewer);
		houghTransformer.registerObserver(transformViewer);
		houghTransformer.registerObserver(controlPanel);
		houghTransformer.registerObserver(projectedHoughSpaceViewer);
		houghTransformer.registerObserver(tableViewer);
		setVisible(true);
	}
	/**
	 * Main Driver for the program
	 * @param args
	 */
	public static void main(String[] args) {
		javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
            	new HoughCircleDetector();
            }
		});
	}
	/* Listens for menu interactions
	 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
	 */
	@Override
	public void actionPerformed(ActionEvent arg0) {
		if (arg0.getSource() == loadImage){
			int returnValue = fileChooser.showOpenDialog(this);
			if (returnValue == JFileChooser.APPROVE_OPTION) {
				try {
					houghTransformer.loadSourceImage(fileChooser.getSelectedFile().getAbsolutePath());
				} catch (IOException e) {
					JOptionPane.showMessageDialog(this,
						    e.getMessage(),
						    "Error loading file",
						    JOptionPane.ERROR_MESSAGE);
					e.printStackTrace();
				}
			}
		} else if (arg0.getSource() == saveCircleCoords){
			fileChooser.setFileFilter(csvFiles);
			int returnValue = fileChooser.showSaveDialog(this);
			if (returnValue == JFileChooser.APPROVE_OPTION) {
				PrintWriter wr;
				try {
					wr = new PrintWriter(new File(fileChooser.getSelectedFile().getAbsolutePath()));
					if (houghTransformer.getDetectedCircles() != null)
						for (Tripple c : houghTransformer.getDetectedCircles())							
							wr.println(c.getX()+","+c.getY()+","+c.getR());
					wr.close();
				} catch (FileNotFoundException e) {
					JOptionPane.showMessageDialog(this,
						    "Could not write file. Destination may be write protected or there may be insufficient space left",
						    "Error writing file",
						    JOptionPane.ERROR_MESSAGE);
				}
			}
			fileChooser.setFileFilter(imageFiles);
		} else if (arg0.getSource() == saveFiltered){
			saveImage(houghTransformer.getFilteredImg());
		} else if (arg0.getSource() == saveHough){
			saveImage(houghTransformer.getProjectedHoughSpaceImg());
		} else if (arg0.getSource() == saveResulting){
			saveImage(houghTransformer.getOutputImg());
		} else if (arg0.getSource() == exit){
			System.exit(0);
		}
	}
	/**
	 * Method to save images using a File Chooser
	 * @param img
	 */
	private void saveImage(BufferedImage img){
		if (img == null){
			JOptionPane.showMessageDialog(this,
				    "The image you've chosen to save is empty. Please load an image and/or transform before exporting",
				    "Empty file",
				    JOptionPane.ERROR_MESSAGE);
			return;
		}
		fileChooser.setFileFilter(pngFiles);
		int returnValue = fileChooser.showSaveDialog(this);
		if (returnValue == JFileChooser.APPROVE_OPTION) {
			try {
				ImageIO.write(img, "png", fileChooser.getSelectedFile());
			} catch (IOException e) {
				JOptionPane.showMessageDialog(this,
					    "Could not write file. Destination may be write protected or there may be insufficient space left",
					    "Error writing file",
					    JOptionPane.ERROR_MESSAGE);
			}
			
		}
		fileChooser.setFileFilter(imageFiles);
	}
	/* Listens for window menu changes
	 * @see javax.swing.event.ChangeListener#stateChanged(javax.swing.event.ChangeEvent)
	 */
	@Override
	public void stateChanged(ChangeEvent arg0) {		
		if (arg0.getSource() == showCircleCoords)
			tableViewer.setVisible(((JCheckBox)arg0.getSource()).isSelected());
		else if (arg0.getSource() == showControlPanel)
			controlPanel.setVisible(((JCheckBox)arg0.getSource()).isSelected());
		else if (arg0.getSource() == showDetectedCircleImage)
			transformViewer.setVisible(((JCheckBox)arg0.getSource()).isSelected());
		else if (arg0.getSource() == showFilteredImage)
			filteredImageViewer.setVisible(((JCheckBox)arg0.getSource()).isSelected());
		else if (arg0.getSource() == showHoughImage)
			projectedHoughSpaceViewer.setVisible(((JCheckBox)arg0.getSource()).isSelected());
		else if (arg0.getSource() == showOriginalImage)
			originalImageViewer.setVisible(((JCheckBox)arg0.getSource()).isSelected());
	}
}
